package view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import logic.GameLogic;
import model.ActionEventWrapper;
import model.Entity;
import model.KeyAction;
import model.Projectile;
import model.Tank;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import util.GameUtil;

public class GameScreen extends JFrame implements ScreenIF
{

	GameLogic gameLogic = new GameLogic(this);

	private BufferStrategy strategy;
	private ArrayList<Entity> entities = new ArrayList<Entity>();
	private ArrayList<Entity> removeList = new ArrayList<Entity>();
	private Entity playerOne;
	private Entity playerTwo;
	private long lastFire = 0;
	private long firingInterval = 500;
	private Logger logger;

	public boolean reset;

	public GameScreen()
	{
		super("Tank Game 101");
		logger = Logger.getLogger(this.getClass().getName());
		PropertyConfigurator.configure(this.getClass().getClassLoader().getResource(GameUtil.LOGGING_CONFIG_URL));
		init();
		gameLogic.gameLoop();
	}

	public void init()
	{
		logger.debug("initializing GameScreen");
		JPanel panel = (JPanel) this.getContentPane();
		panel.setPreferredSize(new Dimension(GameUtil.GAME_SCREEN_WIDTH, GameUtil.GAME_SCREEN_HEIGHT));
		panel.setLayout(null);

		setBounds(0, 0, GameUtil.GAME_SCREEN_WIDTH, GameUtil.GAME_SCREEN_HEIGHT);
		setIgnoreRepaint(true);
		this.pack();
		this.setResizable(false);
		this.setVisible(true);

		this.addWindowListener(new WindowAdapter()
		{
			@Override
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);
			}
		});

		requestFocus();
		createBufferStrategy(2);
		strategy = getBufferStrategy();
		panel.setFocusable(true);

		initEntities();
	}

	private void initEntities()
	{

		playerOne = new Tank("tank_no_gun.png", "tank_gun.png", GameUtil.PLAYER1_INIT_X, GameUtil.PLAYER1_INIT_Y);
		playerOne.setMoveSpeed(GameUtil.TANK_MOVE_SPEED);

		playerTwo = new Tank("tank_no_gun.png", "tank_gun.png", GameUtil.PLAYER2_INIT_X, GameUtil.PLAYER2_INIT_Y);
		playerTwo.setMoveSpeed(GameUtil.TANK_MOVE_SPEED);
		playerTwo.setReversed(true);

		entities.add(playerOne);
		entities.add(playerTwo);

		playerOne.scaleImage(.50);
		playerTwo.scaleImage(.50);

		// JFrame key listeners

		addAction(new ActionEventWrapper(this, new ActionEvent(KeyEvent.VK_F1, -1, "reset"), new Object[] {}, new int[] {}));

		// player one key listeners
		addAction(new ActionEventWrapper(playerOne, new ActionEvent(KeyEvent.VK_A, -1, "left"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerOne, new ActionEvent(KeyEvent.VK_D, -1, "right"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerOne, new ActionEvent(KeyEvent.VK_W, -1, "up"), new Object[] {}, new int[] {}));		
		addAction(new ActionEventWrapper(playerOne, new ActionEvent(KeyEvent.VK_S, -1, "down"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerOne, new ActionEvent(KeyEvent.VK_Q, -1, "rotateUp"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerOne, new ActionEvent(KeyEvent.VK_E, -1, "rotateDown"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerOne, new ActionEvent(KeyEvent.VK_SPACE, -1, "actionOne"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerOne, new ActionEvent(KeyEvent.VK_R, -1, "actionTwo"), new Object[] {}, new int[] {}));

		// //player two key listeners
		addAction(new ActionEventWrapper(playerTwo, new ActionEvent(KeyEvent.VK_LEFT, -1, "left"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerTwo, new ActionEvent(KeyEvent.VK_RIGHT, -1, "right"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerTwo, new ActionEvent(KeyEvent.VK_UP, -1, "up"), new Object[] {}, new int[] {}));		
		addAction(new ActionEventWrapper(playerTwo, new ActionEvent(KeyEvent.VK_DOWN, -1, "down"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerTwo, new ActionEvent(KeyEvent.VK_0, -1, "rotateUp"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerTwo, new ActionEvent(KeyEvent.VK_CONTROL, -1, "rotateDown"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerTwo, new ActionEvent(KeyEvent.VK_PAGE_UP, -1, "actionOne"), new Object[] {}, new int[] {}));
		addAction(new ActionEventWrapper(playerTwo, new ActionEvent(KeyEvent.VK_PAGE_DOWN, -1, "actionTwo"), new Object[] {}, new int[] {}));

	}

	/**
	 * 
	 * @param target
	 *            - The component listening for input
	 * @param actionEvent
	 *            - The input event to listen for, along with the method name to call in the listening class
	 * @param methodArgs
	 *            - parameters to the method being called in the listening class Example methodArds: new Object[]{5, true, "text"}
	 */
	private void addAction(ActionEventWrapper actionEventWrapper)
	{
		List<ActionEventWrapper> actionEventList = new ArrayList<ActionEventWrapper>();
		actionEventList.add(actionEventWrapper);
		KeyAdapter keyAdapter = new KeyAction(actionEventList);
		this.addKeyListener(keyAdapter);
	}

	public void removeEntity(Entity entity)
	{
		removeList.add(entity);
	}

	private void fireCannon(Entity entity)
	{

		boolean reverseShot = entity.isReversed();
		int directionModifier = reverseShot == true ? -1 : 1;

		if (System.currentTimeMillis() - lastFire < firingInterval)
		{
			return;
		}

		double shotScale = 0.6;
		Projectile reference = new Projectile(null, "cannon_ball.png", -100, -100);
		reference.scaleImage(shotScale);

		lastFire = System.currentTimeMillis();
		int posX = entity.getX() + entity.getWidth() + reference.getWidth();
		if (reverseShot)
		{
			posX = entity.getX() - reference.getWidth();
		}
		int posY = entity.getY();
		Projectile shot = new Projectile(this, "cannon_ball.png", posX, posY);
		shot.scaleImage(shotScale);

		shot.setHorizontalMovement(GameUtil.FIRING_SPEED * directionModifier);
		
		entities.add(shot);
	}

	@Override
	public void movePlayerEntities(long delta)
	{
		for (int i = 0; i < entities.size(); i++)
		{
			Entity entity = entities.get(i);
			if (!entity.getClass().equals(Projectile.class))
			{
				entity.setHorizontalMovement(0);
				entity.setVerticalMovement(0);
				entity.setRotationSpeed(0);
			}

			if (entity._moveLeft && !entity._moveRight)
			{
				entity.setHorizontalMovement(-GameUtil.TANK_MOVE_SPEED);
			}
			if (entity._moveRight && !entity._moveLeft)
			{
				entity.setHorizontalMovement(GameUtil.TANK_MOVE_SPEED);
			}
			if (entity._moveUp && !entity._moveDown)
			{
				entity.setVerticalMovement(-GameUtil.TANK_MOVE_SPEED);
			}
			if (entity._moveDown && !entity._moveUp)
			{
				entity.setVerticalMovement(GameUtil.TANK_MOVE_SPEED);
			}
			if (entity._rotateUp)
			{
				entity.setRotationSpeed(-GameUtil.TANK_GUN_ROTATE_SPEED);
			}
			if (entity._rotateDown)
			{
				entity.setRotationSpeed(GameUtil.TANK_GUN_ROTATE_SPEED);
			}
			
			if (entity.getClass().equals(Tank.class))
			{
				if (((Tank) entity).isFiring)
				{
					fireCannon(entity);
				}
			}

			entity.move(delta);
		}
	}

	@Override
	public void drawPlayerEntities(long delta)
	{

		// Get hold of a graphics context for the accelerated
		// surface and blank it out

		Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, GameUtil.GAME_SCREEN_WIDTH, GameUtil.GAME_SCREEN_HEIGHT);

		// cycle round drawing all the entities we have in the game

		for (int i = 0; i < entities.size(); i++)
		{
			Entity entity = entities.get(i);
			entity.draw(g);
		}
		g.dispose();
		strategy.show();
	}

	public void reset()
	{
		entities.clear();
		initEntities();
	}

	public void reset_pressed()
	{
		reset = true;
	}

	public void reset_released()
	{
		reset = false;
	}

	public void handleCollisions()
	{
		for (int p = 0; p < entities.size(); p++)
		{
			for (int s = p + 1; s < entities.size(); s++)
			{
				Entity me = entities.get(p);
				Entity him = entities.get(s);

				if (me.collidesWith(him))
				{
					me.collidedWith(him);
					him.collidedWith(me);
				}
			}
		}

		// remove any entity that has been marked for clear up

		entities.removeAll(removeList);
		removeList.clear();

	}
}
